package cn.demoncat.util.sec;

import cn.demoncat.util.lang.StringUtil;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;

/**
 * AES 对称加密(单密钥算法，密钥可用于加密解密)
 *
 * @author 延晓磊
 *
 * @since 2016年8月11日
 */
public class AESUtil {

	/**
	 * 加密算法
	 */
	public static final String ALGORITHM = "AES";
	/**
	 * 密钥签名算法
	 */
	public static final String SHA1PRNG = "SHA1PRNG";
	
	/**
	 * 密钥长度(AES密钥长度128、192、256位，但受地区出口限制，只能用128位)
	 */
	public static final int KEY_SIZE = 128;
	
	// 错误消息
	private static final String ERROR_KEY = "生成AES密钥失败";
	private static final String ERROR_ENCODE = "AES加密失败";
	private static final String ERROR_DECODE = "AES解密失败";
	
	/**
	 * 生成AES密钥(固定，SHA1签名)
	 * 
	 * @param keyCode	密钥：相同的KEY_CODE永远获取相同的密钥，所以KEY_CODE等同于密钥
	 * 
	 * @return 十六进制编码的密钥
	 * 
	 * @author 延晓磊
	 *
	 * @since 2016年8月11日
	 */
	public static String getKey(String keyCode) {
		try {
			// 实例化密钥生成器
			KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM);
			// SHA1密码随机数供应者（强随机数密钥，如果指定了KEY_CODE则随机数固定）
			SecureRandom secureRandom = SecureRandom.getInstance(SHA1PRNG);
			secureRandom.setSeed(StringUtil.getBytes(keyCode));
			// 初始化
			kg.init(KEY_SIZE, secureRandom);
			// 生成秘密密钥
			SecretKey secretKey = kg.generateKey();
			// 获得密钥的二进制编码形式，再转换为16进制
			return HexUtil.encode(secretKey.getEncoded()) ;
		} catch (Exception e) {
			throw new RuntimeException(ERROR_KEY,e);
		}
	}
	
	/**
	 * 生成AES密钥(随机)
	 * 
	 * @return 十六进制编码的密钥
	 * 
	 * @author 延晓磊
	 *
	 * @since 2016年8月11日
	 */
	public static String getKey() {
		try {
			// 实例化密钥生成器
			KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM);
			// 初始化
			kg.init(KEY_SIZE, new SecureRandom());
			// 生成秘密密钥
			SecretKey secretKey = kg.generateKey();
			// 获得密钥的二进制编码形式，再转换为16进制
			return HexUtil.encode(secretKey.getEncoded()) ;
		} catch (Exception e) {
			throw new RuntimeException(ERROR_KEY,e);
		}
	}
	
	/**
	 * AES加密
	 * 
	 * @param data	源数据
	 * @param key	密钥
	 * @return 加密数据
	 * 
	 * @author 延晓磊
	 *
	 * @since 2016年8月11日
	 */
	public static byte[] encode(byte[] data, String key) {
		// 还原密钥
		SecretKey secretKey = new SecretKeySpec(HexUtil.decode(key), ALGORITHM);
		try {
			// 创建密码器
			Cipher cipher = Cipher.getInstance(ALGORITHM);
			// 初始化密码器，设置为加密模式
			cipher.init(Cipher.ENCRYPT_MODE, secretKey);
			// 加密原数据
			return cipher.doFinal(data);
		} catch (Exception e) {
			throw new RuntimeException(ERROR_ENCODE,e);
		}
	}
 
	/**
	 * AES加密
	 * 
	 * @param text	源数据
	 * @param key	密钥
	 * @return 加密数据
	 * 
	 * @author 延晓磊
	 *
	 * @since 2016年8月11日
	 */
	public static String encode(String text, String key) {
		return Base64Util.encodeUrl(encode(StringUtil.getBytes(text),key));
	}
 
	/**
	 * AES解密
	 *
	 * @param data	加密数据
	 * @param key	密钥
	 * @return 源数据
	 *
	 * @author 延晓磊
	 *
	 * @since 2016年8月11日
	 */
	public static byte[] decode(byte[] data, byte[] key) {
		// 还原为密钥
		SecretKey secretKey = new SecretKeySpec(key, ALGORITHM);
		try {
			// 创建密码器
			Cipher cipher = Cipher.getInstance(ALGORITHM);
			// 初始化密码器，设置为解密模式
			cipher.init(Cipher.DECRYPT_MODE, secretKey);
			// 解密加密数据
			return cipher.doFinal(data);
		} catch (Exception e) {
			throw new RuntimeException(ERROR_DECODE,e);
		}
	}

	/**
	 * AES解密
	 *
	 * @param data	加密数据
	 * @param key	密钥
	 * @return 源数据
	 *
	 * @author 延晓磊
	 *
	 * @since 2016年8月11日
	 */
	public static byte[] decode(byte[] data, String key) {
		return decode(data, HexUtil.decode(key));
	}

	/**
	 * AES解密
	 * 
	 * @param text	加密数据
	 * @param key	密钥
	 * @return 源数据
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月31日
	 */
	public static String decode(String text, String key) {
		return StringUtil.getStr(decode(Base64Util.decode(text), key));
	}
 
	/**
	 * AES加密
	 * 
	 * @param text		源数据
	 * @param keyCode	KEY_CODE
	 * @return 加密数据
	 * 
	 * @author 延晓磊
	 *
	 * @since 2016年8月11日
	 */
	public static String encodeByCode(String text, String keyCode) {
		return encode(text, getKey(keyCode));
	}
	
	/**
	 * AES解密
	 * 
	 * @param text		加密数据
	 * @param keyCode	KEY_CODE
	 * @return 源数据
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月31日
	 */
	public static String decodeByCode(String text, String keyCode) {
		return decode(text, getKey(keyCode));
	}
}
